/**************************************************************************************************
* Title: SimulationManager.cpp
* Authors: Gael Huber, Erin Mooney
* Description: Manages the actual game
**************************************************************************************************/
#include "SimulationManager.h"

// Singleton instance
template<> SimulationManager* Singleton<SimulationManager>::singleton = 0;

// Constructor
SimulationManager::SimulationManager(void) : numEntities(0) {
}	

// Destructor
SimulationManager::~SimulationManager(void) {
}

// Initialize
void SimulationManager::startUp(void) {
	// random seed
	srand(time(0));

	// create the input manager
	inputManager = new InputManager();
	
	// create the render manager
	renderManager = new RenderManager();
	renderManager->startUp();

	// create physics manager
	physicsManager = new PhysicsManager();

	// create 200 entities
	for(int i = 0; i < 200; i++) {
		createRandomEntity();
	}
}

// Shutdown
void SimulationManager::shutDown(void) {
}

/**************************************************************************************************
* Get the singleton instance
**************************************************************************************************/
SimulationManager& SimulationManager::getSingleton(void) { 
	assert(singleton);
	return *singleton;
}

/**************************************************************************************************
* Get the singleton pointer
**************************************************************************************************/
SimulationManager* SimulationManager::getSingletonPtr(void) {
	return singleton;
}

// Create a entity in the center of the screen close to the front (to be added by mouse click)
void SimulationManager::createSpecificEntity(void) {
	createEntity(Vector3(0.0f, 0.0f, -6.0f));
}

// Create a random entity for the first 500
void SimulationManager::createRandomEntity(void) {
	// set an arbitrary starting position
	float x, y, z;
	x = y = z = 0.0f;
	while(x == 0 && y == 0 && z == 0) {
		x = (float)(rand() % 40 - 20);
		y = (float)(rand() % 40 - 20);
		z = -(float)(rand() % 40);
	}
	createEntity(Vector3(x, y, z));
}

// Create an entity
void SimulationManager::createEntity(Vector3 position) {
	// create an object
	Entity* ent;

	// determine its mesh type and create
	Mesh* mesh;
	int randType = rand() % 2;
	if(randType == 0)
		mesh = new Mesh(PYRAMID);
	else
		mesh = new Mesh(CUBE);

	// create physics object
	RigidBody* body = new RigidBody(mesh->rotationAxis, mesh->halfDimensions);
	
	// random velocity
	float x, y, z;
	x = y = z = 0;
	while(x == 0 && y == 0 && z == 0) {
		x = (float)(rand() % 10 - 5) / 100.0f;
		y = (float)(rand() % 10 - 5) / 100.0f;
		z = (float)(rand() % 10 - 5) / 100.0f;
	}
	body->velocity = Vector3(x, y, z);
	
	// random angle
	float angle = 0;
	while(angle == 0) {
		angle = (float)(rand() % 10 - 5); // / 5.0f;
		angle *= PI / 180.0f;
		body->angle = angle;
	}

	// create arbitrary axis of rotation
	x = y = z = 0;
	while(x == 0 && y == 0 && z == 0) {
		x = (float)(rand() % 10 - 5) / 10.0f;
		y = (float)(rand() % 10 - 5) / 10.0f;
		z = (float)(rand() % 10 - 5) / 10.0f;
	}
	body->axisOfRotation = Vector3(x, y, z).getNormalized();

	// set the object's position/screen coordinates based on the vector passed in
	mesh->screenCoords = Vector3(position.x, position.y, position.z);
	body->position = Vector3(position.x, position.y, position.z);

	// Create the entity
	ent = new Entity(mesh, body);
	
	// Add the mesh to the list of objects to render
	renderManager->addToScene(mesh);

	// Add the body to the list of rigid bodies to update
	physicsManager->addToWorld(body);

	// Add to entity list
	entityList.push_back(ent);

	numEntities++;
}

// Doubles the rotation speed of all entities in the world
void SimulationManager::increaseRotationSpeed(void) {
	std::vector<Entity*>::iterator begin = entityList.begin();
	std::vector<Entity*>::iterator end = entityList.end();

	// Iterate through the list change the rotation angle
	for(std::vector<Entity*>::iterator itr = begin; itr != end; ++itr) {
		(*itr)->getBody()->angle += 0.005f;
	}
}

// Halves the rotation speed of all entites in the world
void SimulationManager::decreaseRotationSpeed(void) {
	std::vector<Entity*>::iterator begin = entityList.begin();
	std::vector<Entity*>::iterator end = entityList.end();

	// Iterate through the list change the rotation angle
	for(std::vector<Entity*>::iterator itr = begin; itr != end; ++itr) {
		(*itr)->getBody()->angle -= 0.005f;
	}
}
	
// Doubles the velocity of all entities in the world
void SimulationManager::increaseVelocity(void) {
	std::vector<Entity*>::iterator begin = entityList.begin();
	std::vector<Entity*>::iterator end = entityList.end();

	// Iterate through the list change the velocity
	for(std::vector<Entity*>::iterator itr = begin; itr != end; ++itr) {
		(*itr)->getBody()->velocity.x += 0.005f;
		(*itr)->getBody()->velocity.y += 0.005f;
		(*itr)->getBody()->velocity.z += 0.005f;
	}
}
	
// Half the velocity of all entities in the world
void SimulationManager::decreaseVelocity(void) {
	std::vector<Entity*>::iterator begin = entityList.begin();
	std::vector<Entity*>::iterator end = entityList.end();

	// Iterate through the list change the velocity
	for(std::vector<Entity*>::iterator itr = begin; itr != end; ++itr) {
		(*itr)->getBody()->velocity.x -= 0.005f;
		(*itr)->getBody()->velocity.y -= 0.005f;
		(*itr)->getBody()->velocity.z -= 0.005f;
	}
}

// Update
void SimulationManager::gameLoop(void) {
	MSG msg;	// Windows message
	BOOL done = FALSE;	// Bool variable to exit loop

	while( !done ) {
		// Is there a message waiting?
		if( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) ) {
			// Have we received a quit message?
			if( msg.message == WM_QUIT ) {
				done = TRUE;	// If so, we want to quit
			} else {
				// Otherwise deal with Windows messages
				TranslateMessage(&msg);	// Translate the message
				DispatchMessage(&msg);	// Dispatch the message
			}
		} else { // If there is no message
			// handle input
			inputManager->handleInput();

			// Run if the game is not paused
			if(!getGamePaused()) {
				// Update physics
				physicsManager->update();

				// Post-physics update
				// Define start and end of vector
				std::vector<Entity*>::iterator begin = entityList.begin();
				std::vector<Entity*>::iterator end = entityList.end();

				// Iterate through the list and update each object
				for(std::vector<Entity*>::iterator itr = begin; itr != end; ++itr) {
					// Apply update
					(*itr)->postPhysicsUpdate();
				}
			}

			// Draw the scene
			renderManager->renderScene();
		}
	}
}

// getter for the number of entities in the world
int SimulationManager::getNumEntities(void) { return numEntities; }